package com.kubepartners.impactmodel.translator.graphml;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.kubepartners.impactmodel.translator.GraphReader;
import com.kubepartners.impactmodel.model.Edge;
import com.kubepartners.impactmodel.model.Geometry;
import com.kubepartners.impactmodel.model.Graph;
import com.kubepartners.impactmodel.model.Node;

public class GraphmlReader extends DefaultHandler implements GraphReader {
	// default graph
	private Graph graph = new Graph(Edge.class);

	// state engine
	private boolean inGraphml = false;
	private boolean inGraph = false;
	private boolean inNode = false;
	private boolean inData = false;
	private boolean inEdge = false;
	private Node currentNode = null;
	private Edge currentEdge = null;
	private String currentString = "";

	// Parser calls this for each element in a document
	public void startElement(String namespaceURI, String localName,	String qName, Attributes atts) throws SAXException {
		if (localName.equals("graphml")) {
			inGraphml = true;
		}
		if (localName.equals("graph")) {
			if (inGraphml) {
				inGraph = true;
			}
		}
		if (localName.equals("node")) {
			if (inGraph) {
				inNode = true;
				currentNode = new Node(atts.getValue("id"));
			}
		}
		if (localName.equals("data")) {
			inData = true;
		}
		if (localName.equals("edge")) {
			if (inGraphml) {
				inEdge = true;
				currentEdge = new Edge(atts.getValue("id"));
				Node source = graph.findNodeById(atts.getValue("source"));
				Node target = graph.findNodeById(atts.getValue("target"));
				boolean e=graph.addEdge(source, target, currentEdge);
				if(!e){
					source.setMultipleLinksError(true);
					target.setMultipleLinksError(true);
				}
			}
		}
		if (localName.equals("Geometry") && inNode) {
			currentNode.setGeometry(new Geometry(atts.getValue("x"), atts.getValue("y")));
		}
	}

	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
		if (localName.equals("graphml")) {
			inGraphml = false;
		}
		if (localName.equals("graph")) {
			inGraph = false;
		}
		if (localName.equals("node")) {
			inNode = false;
			currentNode = null;
		}
		if (localName.equals("data")) {
			if (currentString.length() > 0) {
				// Get map of key/value pairs
				Map<String, String> m = decodeText(currentString);
				// Check for Node data if conditions met
				if (inNode && inData && currentNode != null) {
					try {
						// If the data node text does not translate to a map (i.e no csv's),
						// then just set the external ID of the node to the text
						if (m.size() == 0) {
							currentNode.setExternalID(currentString);
						}
						// Add node to graph, providing attributes exist
						if (m.containsKey("vc")) {
							double capacity = Double.parseDouble((String) m.get("vc"));
							currentNode.setVertexCapacity(capacity);
						}
						if (m.containsKey("ic")) {
							double capacity = Double.parseDouble((String) m.get("ic"));
							currentNode.setInducedCapacity(capacity);
						}
						if (m.containsKey("dc")) {
							double capacity = Double.parseDouble((String) m.get("dc"));
							currentNode.setDeliveredCapacity(capacity);
						}
						if (m.containsKey("description")) {
							currentNode.setDescription(m.get("description"));
						}
						if (m.containsKey("id")) {
							currentNode.setExternalID(m.get("id"));
						}
						// 'label' in map overwrites yFiles NodeLabel element
						if (m.containsKey("label")) {
							currentNode.setLabel(m.get("label"));
						}
						if (m.containsKey("measurable") && m.get("measurable").equals("true")) {
							currentNode.setMeasurable(true);
						}
						// Finally, add the node
						graph.addVertex(currentNode);
					} catch (Exception e) {
						e.printStackTrace();
						throw new SAXException("Error saving Node!", e);
					}
				} else
				// Check for Edge data
				if (inEdge && inData && currentEdge != null) {
					try {
						if (m.containsKey("w")) {
							double weight = Double.parseDouble((String) m.get("w"));
							currentEdge.setWeight(weight);
						}
						if (m.containsKey("t")) {
							double threshold = Double.parseDouble((String) m.get("t"));
							currentEdge.setThreshold(threshold);
						}
					} catch (Exception e) {
						e.printStackTrace();
						throw new SAXException("Error saving Edge!", e);
					}
				}
				// Reset state
				inData = false;
				currentString = "";
			}
		}
		if (localName.equals("edge")) {
			inEdge = false;
		}
		if (localName.equals("NodeLabel")) {
			if (currentString.length() > 0) {
				currentNode.setLabel(currentString);
			}
		}
	}

	public void characters(char[] ch, int start, int length) throws SAXException {
		currentString = currentString + String.valueOf(ch, start, length);
	}

	// Create HashMap of key/value pairs
	private Map<String, String> decodeText(String text) {
		HashMap<String, String> map = new HashMap<String, String>();
		StringTokenizer st = new StringTokenizer(text, ",");
		while (st.hasMoreTokens()) {
			String element = st.nextToken();
			if (element.contains("=")) {
				String key = element.substring(0, element.indexOf("=")).trim();
				String value = element.substring(element.indexOf("=") + 1).trim();
				map.put(key, value);
			}
		}
		return map;
	}

	public Graph readGraph() {
		return graph;
	}

	public Graph readGraph(InputStream is) {
		// TODO Auto-generated method stub
		return graph;
	}

	public void setGraph(Graph graph) {
		// TODO Auto-generated method stub
		
	}
}
